package com.atguigu.exam.service.impl;

import com.atguigu.exam.common.CacheConstants;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.entity.QuestionAnswer;
import com.atguigu.exam.entity.QuestionChoice;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionPageVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import kotlin.jvm.internal.Lambda;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.validation.Schema;
import java.io.IOException;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;
    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;
    @Autowired
    private PaperQuestionMapper paperQuestionMapper;
    /**
     * 分页查询题目数据
     *
     * @param pageBean
     * @param questionPageVo
     */
    @Override
    public void customPageService(IPage<Question> pageBean, QuestionPageVo questionPageVo) {
        questionMapper.customPage(pageBean,questionPageVo);
    }

    /**
     * 根据ID查询题目详情
     * @param id questionId
     * @return 题目详情
     */
    @Override
    public Question customDetailQuestion(Long id) {
        //1.查询题目详情
        Question question = questionMapper.customGetById(id);
        if (question==null){
            throw new RuntimeException("题目详情查询失败，题目id为："+id);
        }
        //2.进行热点题目缓存
        new Thread(() -> {
            incrementQuestion(question.getId());
        }).start();
        return question;
    }


    /**
     * 创建新题目
     * @param question
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customSaveQuestion(Question question) {
        //1.同一类型下，题目title不能相同
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Question::getType,question.getType());
        lambdaQueryWrapper.eq(Question::getTitle,question.getTitle());
        long count = count(lambdaQueryWrapper);
        if (count>0){
            //在同一题目类型下存在相同题目名称，更新题目信息失败
            throw new RuntimeException("在%s题目类型下存在题目%s，更新题目信息失败".formatted(question.getType(),question.getTitle()));
        }
        //2.保存题目信息
        boolean result = save(question);
        //3.获取答案对象
        QuestionAnswer answer = question.getAnswer();
        answer.setQuestionId(question.getId());
        //4.判断是否是选择题
        if("CHOICE".equals(question.getType())){
            List<QuestionChoice> choices = question.getChoices();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choices.size(); i++) {
                QuestionChoice questionChoice = choices.get(i);
                questionChoice.setQuestionId(question.getId());
                questionChoice.setSort(i);
                questionChoiceMapper.insert(questionChoice);
                if (questionChoice.getIsCorrect()){
                    //true 正确答案
                    if (sb.length()>0){
                        sb.append(",");
                    }
                    sb.append((char)('A'+i));
                }
            }
            //进行答案赋值
            answer.setAnswer(sb.toString());
        }
        //保存答案对象
        questionAnswerMapper.insert(answer);
    }

    /**
     * 修改题目信息
     * @param question
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customUpdateQuestion(Question question) {
        //1.判断 修改题目信息后同一题目类型下不能出现相同title的题目
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Question::getTitle,question.getTitle());
        lambdaQueryWrapper.ne(Question::getId,question.getId());
        long count = count(lambdaQueryWrapper);
        if(count>0){
            throw new RuntimeException("%s和其他题目重复，修改失败".formatted(question.getTitle()));
        }
        //2.修改题目
        boolean result = updateById(question);
        if(!result){
            throw new RuntimeException("修改%s题目失败".formatted(question.getId()));
        }
        //3.获取答案对象
        QuestionAnswer answer = question.getAnswer();
        //4.判断是否是选择题
        if ("CHOICE".equals(question.getType())){
            List<QuestionChoice> choiceList = question.getChoices();
            //删除题目对应的所有原选项
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
            lambdaQueryWrapper1.eq(QuestionChoice::getQuestionId,question.getId());
            questionChoiceMapper.delete(lambdaQueryWrapper1);
            //循环新增选项
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choiceList.size(); i++) {
                QuestionChoice questionChoice = choiceList.get(i);
                questionChoice.setId(null);
                questionChoice.setCreateTime(null);
                questionChoice.setUpdateTime(null);
                questionChoice.setSort(i);
                questionChoice.setQuestionId(question.getId());
                questionChoiceMapper.insert(questionChoice);
                //拼接正确答案
                if (questionChoice.getIsCorrect()){
                    if(sb.length()>0){
                        sb.append(",");
                    }
                    sb.append((char)('A'+i));
                }
            }
            //答案对象赋值选择题答案
            answer.setAnswer(sb.toString());
        }
        //5.进行答案的修改
        questionAnswerMapper.updateById(answer);
        //6.添加事务
    }

    /**
     * 删除题目
     * @param id
     */
    @Override
    public void customRemoveQuestionById(Long id) {
        //1.如果题目被试卷引用，则不能删除该题目
        LambdaQueryWrapper<PaperQuestion> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PaperQuestion::getQuestionId,id);
        Long count = paperQuestionMapper.selectCount(lambdaQueryWrapper);
        if (count>0){
            throw new RuntimeException("题目%s被试卷引用%s次，不能删除该题目".formatted(id,count));
        }
        //2.删除题目
        boolean result = removeById(id);
        if (!result){
            throw new RuntimeException("该题目%s删除失败");
        }
        //3.删除题目关联的答案，选项
        LambdaQueryWrapper<QuestionAnswer> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.eq(QuestionAnswer::getQuestionId,id);
        questionAnswerMapper.delete(lambdaQueryWrapper1);
        LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper2 = new LambdaQueryWrapper<>();
        lambdaQueryWrapper2.eq(QuestionChoice::getQuestionId,id);
        questionChoiceMapper.delete(lambdaQueryWrapper2);
    }

    /**
     * 题目批量数据预览
     *
     * @param file
     * @return
     */
    @Override
    public List<QuestionImportVo> preViewExcel(MultipartFile file) throws IOException {
        //数据校验
        if (file==null || file.isEmpty()){
            throw new RuntimeException("预览数据的文件为空");
        }
        String fileName = file.getOriginalFilename();
        //判断文件格式
        if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")){
            throw new RuntimeException("预览数据的文件格式错误，必须是.xls或.xlsx");
        }
        //解析数据
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //返回结果
        return questionImportVoList;
    }

    /**
     * 获取热门题目
     * @param size
     * @return List<Question>
     */
    @Override
    public List<Question> customFindPopularQuestions(Integer size) {
        //1.定义热门题目集合 总集合
        List<Question> popularQuestion = new ArrayList<>();
        //2.去zset中获取热门题目，并且添加到总集合中
        Set<Object> idsSet = redisUtils.zReverseRange(CacheConstants.POPULAR_QUESTIONS_KEY, 0, size - 1);
        System.out.println("idsSet = " + idsSet);
        if (idsSet!=null && !idsSet.isEmpty()){
            //将 Set<Object> 转换为 List<Long>
            List<Long> idsList = idsSet.stream()
                    .map(obj -> Long.valueOf(obj.toString()))
                    .collect(Collectors.toList());
            //System.out.println("idsList = " + idsList);
            for (int i = 0; i < idsList.size(); i++) {
                Question question = getById(idsList.get(i));
                if(question!=null){
                    popularQuestion.add(question);
                }
            }
        }
        //3.检查题目数量是否等于size
        int diff = size-popularQuestion.size();
        if(diff>0){
            //4.题目数量不够size，需要从Question中补充
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.notIn(Question::getId,popularQuestion);
            lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            lambdaQueryWrapper.last("limit "+diff);
            List<Question> questionList = list(lambdaQueryWrapper);
            log.debug("Question表中补充热门题目，题目数量{}，题目内容{}",questionList.size(),questionList);
            if (!questionList.isEmpty()){
                //5.补充题目到总集合中
                popularQuestion.addAll(questionList);
            }
        }
        //6.总集合一起进行答案和选项填充
        fillQuestionChoiceAndAnswer(popularQuestion);
        //7.返回
        return popularQuestion;
    }

    /**
     * 将题目列表批量导入到数据库，支持Excel解析后的导入或AI生成后的确认导入
     * @param questions
     * @return 导入成功的数量
     */
    @Override
    public int importBatchQuestions(List<QuestionImportVo> questions) {
        //1.进行数据校验
        if(questions==null || questions.isEmpty()){
            throw new RuntimeException("导入的题目集合为空");
        }
        //循环+try catch 允许内部有失败
        int successCount = 0;
        for (int i = 0; i < questions.size(); i++) {
            try {
                //QuestionImportVo接收的数据赋给Question 提取一个方法
                Question question = convertQuestionImportVoToQuestion(questions.get(i));
                //单条数据保存成功
                customSaveQuestion(question);
                //计数统计
                successCount++;
            }catch(Exception e) {
                log.debug("{}题目导入失败",questions.get(i).getTitle());
            }
        }
        return successCount;
    }

    private Question convertQuestionImportVoToQuestion(QuestionImportVo questionImportVo){
        //1.给question本体属性赋值
        Question question = new Question();
        BeanUtils.copyProperties(questionImportVo,question);
        //2.判断 如果是选择题，给选项集合进行赋值
        if ("CHOICE".equals(questionImportVo.getType())){
            //判断questionImportVo的选项个数
            if (!questionImportVo.getChoices().isEmpty()){
                List<QuestionChoice> questionChoices = new ArrayList<>(questionImportVo.getChoices().size());
                for (QuestionImportVo.ChoiceImportDto importVoChoice : questionImportVo.getChoices()) {
                    QuestionChoice questionChoice = new QuestionChoice();
                    questionChoice.setContent(importVoChoice.getContent());
                    questionChoice.setIsCorrect(importVoChoice.getIsCorrect());
                    questionChoice.setSort(importVoChoice.getSort());
                    questionChoices.add(questionChoice);
                }
                question.setChoices(questionChoices);
            }
        }
        //3.不管是不是选择题创建答案对象并赋值
        QuestionAnswer questionAnswer = new QuestionAnswer();
        //如果是选择题，需要将true/false转换成大写
        if("CHOICE".equals(questionImportVo.getType())){
            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
        }else{
            questionAnswer.setAnswer(questionImportVo.getAnswer());
        }
        questionAnswer.setKeywords(questionImportVo.getKeywords());
        question.setAnswer(questionAnswer);

        return question;
    }

    /**
     * 进行题目批量导入
     * @param file
     */
    @Override
    public String importExcelBatchQuestions(MultipartFile file) throws IOException {
        //1.检验
        if (file==null || file.isEmpty()){
            throw new RuntimeException("批量导入的文件为空");
        }
        String fileName = file.getOriginalFilename();
        if(!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")){
            throw new RuntimeException("批量导入的文件格式错误，必须为xls或者xlsx");
        }
        //2.解析文件
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //3.调用批量导入业务
        int successCount = importBatchQuestions(questionImportVoList);
        String result = "批量导入题目接口调用成功，一共%s道题目需要导入，成功导入了%s道题目".formatted(questionImportVoList.size(),successCount);
        return result;
    }

    //定义进行题目访问次数增长的方法
    private void incrementQuestion(Long questionId){
        Double score = redisUtils.zIncrementScore(CacheConstants.POPULAR_QUESTIONS_KEY, questionId, 1);
        log.info("完成{}题目分数累计，累计后分数为：{}",questionId,score);
    }

    //Java代码进行分页查询，批量为题目列表填充选项和答案
    private void fillQuestionChoiceAndAnswer(List<Question> questionList){
        //1.如果题目列表为空，则返回
        if (questionList==null || questionList.isEmpty()){
            log.debug("题目列表为空");
            return;
        }
        //2.查询分页显示的题目对应的ID
        List<Long> ids = questionList.stream().map(Question::getId).collect(Collectors.toList());
        //3.查询所有选项
        LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(QuestionChoice::getQuestionId,ids);
        List<QuestionChoice> questionChoiceList = questionChoiceMapper.selectList(lambdaQueryWrapper);
        //4.查询所有答案
        LambdaQueryWrapper<QuestionAnswer> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.in(QuestionAnswer::getQuestionId,ids);
        List<QuestionAnswer> questionAnswerList = questionAnswerMapper.selectList(lambdaQueryWrapper1);
        //5.按题目ID分组选项
        Map<Long, List<QuestionChoice>> choiceMap = questionChoiceList.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
        //6.按题目ID分组答案
        Function<QuestionAnswer,Long> fun1 = new Function<QuestionAnswer, Long>() {
            @Override
            public Long apply(QuestionAnswer questionAnswer) {
                return questionAnswer.getQuestionId();
            }
        };
        Function<QuestionAnswer,QuestionAnswer> fun2 = new Function<QuestionAnswer, QuestionAnswer>() {
            @Override
            public QuestionAnswer apply(QuestionAnswer questionAnswer) {
                return questionAnswer;
            }
        };
        Map<Long, QuestionAnswer> answerMap = questionAnswerList.stream().collect(Collectors.toMap(fun1, fun2));
        //7.循环题目集合，进行选项和答案填充
        Consumer<Question> c = new Consumer<Question>(){
            @Override
            public void accept(Question question) {
                //每个题目都有一个答案
                question.setAnswer(answerMap.get(question.getId()));
                //选择题才有选项
                if("CHOICE".equals(question.getType())){
                    List<QuestionChoice> questionChoices = choiceMap.get(question.getId());
                    questionChoices.sort(Comparator.comparingInt(QuestionChoice::getSort));
                    question.setChoices(questionChoices);
                }
            }
        };
        questionList.forEach(c);
    }
}